home *** CD-ROM | disk | FTP | other *** search
/ The Fatted Calf / The Fatted Calf.iso / Applications / Communication / NewsBase / Source / IItemHeaderBrowser.m < prev    next >
Text File  |  1993-01-12  |  12KB  |  417 lines

  1. /*$Copyright:
  2.  * Copyright (C) 1992.5.22. Recruit Co.,Ltd. 
  3.  * Institute for Supercomputing Research
  4.  * All rights reserved.
  5.  * NewsBase  by ISR, Kazuto MIYAI, Gary ARAKAKI, Katsunori SUZUKI, Kok-meng Lue
  6.  *
  7.  * You may freely copy, distribute and reuse the code in this program under 
  8.  * following conditions.
  9.  * - to include this notice in the source code, if it is to be distributed 
  10.  *   with source code.
  11.  * - to add the file named "COPYING" within the code, which shall include 
  12.  *   GNU GENERAL PUBLIC LICENSE(*).
  13.  * - to display an acknowledgement in binary code as follows: "This product
  14.  *   includes software developed by Recruit Co.,Ltd., ISR."
  15.  * - to display a notice which shall state that the users may freely copy,
  16.  *   distribute and reuse the code in this program under GNU GENERAL PUBLIC
  17.  *   LICENSE(*)
  18.  * - to indicate the way to access the copy of GNU GENERAL PUBLIC LICENSE(*)
  19.  *
  20.  *   (*)GNU GENERAL PUBLIC LICENSE is stored in the file named COPYING
  21.  * 
  22.  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
  23.  * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
  24.  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  25. $*/
  26.  
  27. #import <appkit/appkit.h>
  28. #import <mach/mach_init.h>
  29. #import <string.h>
  30. #import <defaults/defaults.h>
  31. #import <dpsclient/event.h>
  32. #import <libc.h>
  33.  
  34. #import "IItemHeaderBrowser.h"
  35. #import "INXBrowserCellWithLinkedObject.h"
  36. #import "IUifNode.h"
  37. #import "IOrderedListD.h"
  38. #import "InfoD.h"
  39. #import "INewsGroupTreeControl.h"
  40. #import "INewsgroupInfoD.h"
  41. #import "ITreeNodeD.h"
  42. #import "ITreeBrowserMatrix.h"
  43. #import "errdebug.h"
  44. #import "data_types.h"
  45. #import "Localization.h"
  46.  
  47. #define        MAX_NO_OF_TOKENS    256
  48. #define     MAXVISIBLECOL     2
  49. #define     MINCOLWIDTH    160
  50.  
  51. extern id currentNewsGroup;
  52.  
  53. @implementation IItemHeaderBrowser
  54.  
  55. - initFrame:(NXRect *)frameRect
  56. {
  57.     [super initFrame:frameRect];
  58.     [self setMinColumnWidth:MINCOLWIDTH];
  59.     [self setMaxVisibleColumns:MAXVISIBLECOL];
  60.     [self setAction:@selector(singleClickLeafPreHandler:)];
  61.     [self setDoubleAction:@selector(doubleClickLeafPreHandler:)];
  62.  
  63.     [self acceptArrowKeys:YES andSendActionMessages:NO];
  64.     return self;
  65. }
  66.  
  67. - (BOOL)acceptsFirstResponder
  68. {
  69.     return YES;
  70. }
  71.  
  72. - loadColumnZero
  73. {
  74.     // make self first responder when loading column and column has
  75.     // any cells. if self(browser) has no column, then refuse to get 
  76.     // first responder. data group browser might get first responder.
  77.     [super loadColumnZero];
  78.     if ([[[self matrixInColumn:0] cellList] count] > 0) {
  79.     [window makeFirstResponder:self];
  80.     }
  81.     return self;
  82. }
  83.  
  84. /*
  85.     The target object and action message of itemheader is IItemHeaderBrowser
  86.     and articleHandler.  I.e., articleHandler is called when a article is
  87.     selected.
  88. */
  89. - (void)singleClickLeafPreHandler:sender
  90. {
  91.     ITreeBrowserMatrix    *lastColumnMatrix;
  92.     int        keyFlag;
  93.  
  94.     lastColumnMatrix = [self matrixInColumn:[self lastColumn]];
  95.     if (((keyFlag=(int)[lastColumnMatrix flagsOfLastEvent]) & NX_COMMANDMASK)
  96.                         && (keyFlag & NX_CONTROLMASK)) {
  97.     // 'command' + 'control' click
  98.     // post cancel article
  99.     if (NXRunAlertPanel(LoStr("NewsBase"),
  100.         LoStr("Are you sure to cancel the article?"),
  101.         LoStr("NO"),LoStr("YES"),NULL) == 0) {
  102.         [lastColumnMatrix resetFlagsOfLastEvent];
  103.         [self cancelArticle];
  104.         return;
  105.     } else {
  106.         /* Cancel is clicked */
  107.         return;
  108.     }
  109.     }
  110.  
  111.     if (strcmp(NXGetDefaultValue(OWNER, ARTICLESELECT),SINGLECLICK)==0) {
  112.     [self leafPreHandler:sender];
  113.     return;
  114.     } else {
  115. //        lastColumnMatrix = [self matrixInColumn:[self lastColumn]];
  116.     if (keyFlag & NX_CONTROLMASK) {
  117.         DBG(1,fprintf(stderr,"-- IItemHeaderBrowser:singleClick"));
  118.         // don't have to reset flags in matrix because "leafPreHander:"
  119.         // will do it in next statement.
  120.         [self leafPreHandler:sender];
  121.         return;
  122.     }
  123.     return;
  124.     }
  125. }
  126.  
  127. - (void)doubleClickLeafPreHandler:sender
  128. {
  129.     if (strcmp(NXGetDefaultValue(OWNER, ARTICLESELECT),DOUBLECLICK)==0) {
  130.     [self leafPreHandler:sender];
  131.     return;
  132.     } else {
  133.     return;
  134.     }
  135. }
  136.  
  137. - (void)leafHandler:sender
  138. {
  139.     id         selectedCell;
  140.     id        articleItem;
  141.     id        node;
  142.  
  143.     [window makeFirstResponder:self];    // set first responder to self
  144.     selectedCell = [[self matrixInColumn:[self lastColumn]] selectedCell];
  145.  
  146.     if ([selectedCell isLeaf]) {
  147.         node = [selectedCell node];
  148.         if ((articleItem = [iIOmodule itemOf:node])==nil) {
  149.         return;
  150.     }
  151.     
  152.     /* set no active to cell */
  153.     if (([node active]==YES) && ([iIOmodule toggleActive:node]==YES)) {
  154.             [selectedCell setActive:NO];
  155.         [node setActive:NO];
  156.     }
  157.     [[self matrixInColumn:[self lastColumn]] display];
  158.     return;
  159.     }
  160.     return;
  161. }
  162.  
  163. - directoryHandler:node
  164. {
  165.     [window makeFirstResponder:self];    // set first responder to self
  166.     return [iIOmodule itemHeadersOf:node];
  167. }
  168.  
  169. - keyDown:(NXEvent *)theEvent
  170. {
  171.     unsigned int    ch;
  172.     id        selectedCell;
  173.     
  174.     DBG(1,fprintf(stderr,"  keydown"));
  175.     selectedCell = [[self matrixInColumn:[self selectedColumn]] selectedCell];
  176.  
  177.     if ((ch=(theEvent->data).key.charCode) == NX_CR) {
  178.     // "Return" key pressed
  179.     DBG(1,fprintf(stderr,"  NX_CR"));
  180.     // click selected cell
  181.         if ([selectedCell isLeaf] ==YES) {
  182.         [self leafHandler:self];
  183.     }
  184.     return self;
  185.     }
  186.     if (ch == 0x20 || ch == 0x80) {
  187.     //  0x20 : space
  188.     //  0x80 : alt+space
  189.     if ([self selectedColumn] == -1) {
  190.         // no cell is selected so select first one
  191.         [[self matrixInColumn:0] selectCellAt:0 :0];
  192.     }
  193.     selectedCell = [[self matrixInColumn:[self selectedColumn]]
  194.                                 selectedCell];
  195.     if ((theEvent->flags & NX_SHIFTMASK) == NO) {
  196.         // select next cell
  197.         if (theEvent->flags & NX_ALTERNATEMASK) {
  198.         // alternate key is down, select next cell
  199.         selectedCell = [self selectNextCell:selectedCell];
  200.         } else {
  201.         // start loop for selecting next *active* *leaf* cell
  202.         do {
  203.             if ([selectedCell isLeaf] == YES
  204.                 && [[selectedCell node] active] == YES) {
  205.             break;
  206.             }
  207.             selectedCell = [self selectNextCell:selectedCell];
  208.         } while (selectedCell != NULL);
  209.         }
  210.     } else {
  211.         // select prev cell
  212.         DBG(1,fprintf(stderr,"--- space+shift\n"));
  213.         [self selectPrevCell:selectedCell];
  214.     }
  215.     if (selectedCell != NULL) {
  216.         if ((theEvent->flags & NX_ALTERNATEMASK)==NO
  217.              && (theEvent->flags & NX_SHIFTMASK)==NO) {
  218.         // cell is selected and alt key is not down, click cell
  219.         [self leafHandler:self];
  220.         }
  221.         return self;
  222.     } else {
  223.         // no more cells, so beep
  224.         return ([super keyDown:theEvent]);
  225.     }
  226.     }
  227.  
  228.     // handle arrow keys in super class
  229.     return ([super keyDown:theEvent]);
  230. }
  231.  
  232. - selectNextCell:nowselectedCell
  233. {
  234.     return([self _selectCell:nowselectedCell direct:(DirectFlag)FORWARD]);
  235. }
  236.  
  237. - selectPrevCell:nowselectedCell
  238. {
  239.     return([self _selectCell:nowselectedCell direct:(DirectFlag)BACKWARD]);
  240. }
  241.  
  242. - _selectCell:nowselectedCell direct:(DirectFlag)dflag
  243. {
  244.     unsigned int    cellIndex;
  245.     id        selectedColumnMatrix;
  246.     id        rightColumnMatrix, leftColumnMatrix;
  247.     int        trow, tcol;        // can not use cellList???
  248.     int        tcellIndex;
  249.     // cellList has old information and sometimes it points cell which
  250.     // is alread deleted
  251.     
  252.     selectedColumnMatrix = [self matrixInColumn:[self selectedColumn]];
  253.     
  254.     if ([nowselectedCell isLeaf] == YES) {
  255.     // no right column
  256.     cellIndex = [[selectedColumnMatrix cellList] indexOf:nowselectedCell];
  257.     [selectedColumnMatrix getNumRows:&trow numCols:&tcol];
  258.  
  259.     // check if selectedCell is bottom(for FORWARD) or top(for BACKWORD)
  260.     if ((dflag == FORWARD && cellIndex < (trow -1))
  261.             || (dflag == BACKWARD && cellIndex != 0)) {
  262.         // select next cell
  263.         if (dflag == FORWARD) {
  264.         tcellIndex = cellIndex + 1;
  265.         } else {
  266.         tcellIndex = cellIndex - 1;        
  267.         }
  268.         [selectedColumnMatrix selectCellAt:tcellIndex :0];
  269.         [self scrollToCellAt:tcellIndex matrix:selectedColumnMatrix];
  270.     } else {
  271.         // this cell is bottom or top
  272.         if ((leftColumnMatrix=[self matrixInColumn:
  273.                     ([self selectedColumn] -1)]) != nil) {
  274.         // select next cell in left column
  275.         cellIndex = [[leftColumnMatrix cellList]
  276.                 indexOf:[leftColumnMatrix selectedCell]];
  277.         [leftColumnMatrix getNumRows:&trow numCols:&tcol];
  278.         if ((dflag == FORWARD && cellIndex < (trow -1))
  279.                 || (dflag == BACKWARD && cellIndex != 0)) {
  280.             if (dflag == FORWARD) {
  281.             tcellIndex = cellIndex + 1;
  282.             } else {
  283.             tcellIndex = cellIndex - 1;
  284.             }
  285.             [leftColumnMatrix selectCellAt:tcellIndex :0];
  286.             [self setLastColumn:0];
  287.             [self scrollToCellAt:tcellIndex matrix:leftColumnMatrix];
  288.         } else {
  289.             // left cell is last one
  290.             [self setLastColumn:0];
  291.             return NULL;
  292.         }
  293.         } else {
  294.         // no left column
  295.         // this cell is really end
  296.         return NULL;
  297.         }
  298.     }
  299.     } else {
  300.     // there is right column, but no cell is selected
  301.     // select first cell in right column
  302.     [selectedColumnMatrix lockFocus];
  303.     [selectedColumnMatrix sendAction];
  304.     [selectedColumnMatrix unlockFocus];
  305.     rightColumnMatrix=[self matrixInColumn:([self selectedColumn] +1)];
  306.     if (dflag == FORWARD) {
  307.         [rightColumnMatrix selectCellAt:0 :0];
  308.     } else {
  309.         [rightColumnMatrix selectCellAt:
  310.                         ([rightColumnMatrix cellCount]-1) :0];
  311.     }
  312.     }
  313.     
  314.     // return id of selectedCell
  315.     return ([[self matrixInColumn:[self selectedColumn]] selectedCell]);
  316. }
  317.  
  318. - scrollToCellAt:(int)row matrix:matrix
  319. {
  320.     NXRect    matrixRect, cellRect;
  321.     id        contView;    // clipView
  322.     id        scrollView;    // scollView of a column for NXBrowser
  323.     NXRect    contRect;
  324.     NXRect    scrollerRect;
  325.     NXCoord    cell_y, cont_y;
  326.     
  327.     contView = [matrix superview];
  328.     scrollView = [[matrix superview] superview];
  329.     
  330.     [matrix getFrame:&matrixRect];
  331.     [matrix getCellFrame:&cellRect at:row :0];
  332.     [contView getFrame:&contRect];
  333.     
  334.     cell_y = cellRect.origin.y + cellRect.size.height;
  335.     cont_y = contRect.size.height - cellRect.size.height * 3;
  336.             // 3 cells are always shown after cell in (int)row
  337.     if (cont_y < cell_y) {
  338.     contRect.origin.y = cell_y - cont_y;
  339.     }
  340.     [[scrollView vertScroller] getFrame:&scrollerRect];
  341.     contRect.origin.x = 0.0;
  342.     [contView rawScroll:&(contRect.origin)];
  343.     [scrollView reflectScroll:contView];
  344.  
  345.     return (self);
  346. }
  347.     
  348.     
  349.  
  350. - cancelArticle
  351. {
  352.     id        selectedCell;
  353.     id        node;
  354.     id        header;
  355.     const char    *messageID;
  356.     char    *fromValue;
  357.     char    buf[256], *user_name;
  358.     char    *mailAddress;
  359.  
  360.     selectedCell = [[self matrixInColumn:[self lastColumn]] selectedCell];
  361.     node = [selectedCell node];
  362.     if ((header=[[node linkedData] dataForKey:HEADER_INFO]) == nil) {
  363.     return (nil);
  364.     }
  365.     if ((fromValue=(char *)[header infoForKey:FROM]) == NULL) {
  366.     return (nil);
  367.     }
  368.     // check authentication
  369.     sscanf(fromValue, "%256[^@]", buf);
  370.     DBG(1,fprintf(stderr,"  buf = %s\n", buf));
  371.     if ((user_name=getenv("USER")) == NULL) {
  372.     return (nil);
  373.     }
  374.     if (strcmp(user_name,buf)!=0) {
  375.         NXRunAlertPanel(LoStr("NewsBase"),LoStr("From: field value not matched. Can not cancel"),NULL,NULL,NULL);    
  376.     return (nil);
  377.     } 
  378.  
  379.     if ((messageID = (const char *)[header infoForKey:MESSAGE_ID]) == NULL) {
  380.     return (nil);
  381.     }
  382.     
  383.     if (NXGetDefaultValue(OWNER,FROM) != NULL) {
  384.     mailAddress = NXCopyStringBuffer(NXGetDefaultValue(OWNER,FROM));
  385.     } else {
  386.     // set default value
  387.     mailAddress = [self getMailAddress];
  388.     }
  389.     
  390.     [iIOmodule cancelArticleMessageID:messageID from:mailAddress];
  391.     free(mailAddress);
  392.     return self;
  393. }
  394.  
  395. - (char *)getMailAddress
  396. {
  397.     char    *mail_address;
  398.     char    *user_name, machine_name[256], domain_name[257];
  399.  
  400.     user_name = NXCopyStringBufferFromZone(getenv("USER"),[self zone]);
  401.     gethostname(machine_name,sizeof(machine_name));
  402.     getdomainname(domain_name + 1, sizeof(domain_name) - 1);
  403.     domain_name[sizeof(domain_name) - 1] = '\0';
  404.     if (domain_name[1] != '\0') {
  405.         domain_name[0] = '.';
  406.     } else {
  407.         domain_name[0] = '\0';
  408.     }
  409.     mail_address = (char *)NXZoneMalloc([self zone],
  410.         strlen(user_name)+strlen(machine_name)+strlen(domain_name)+3);
  411.     (void)sprintf(mail_address,"%s@%s%s",user_name,machine_name,
  412.                                 domain_name);
  413.     return mail_address;
  414. }
  415.  
  416. @end
  417.